package com.example.gateway.predicate;

import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 自定义断言工厂：根据请求参数的值来判断路由是否匹配
 * Gateway 会根据类名自动去掉后缀 RoutePredicateFactory，因此你在 YAML 中使用时的断言名字就是 Vip
 */
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {

    /**
     * 指定断言所用配置类为内部静态类 Config
     * 该类的字段会通过 YAML 映射成参数
     */
    public VipRoutePredicateFactory() {
        super(Config.class);
    }

    /**
     * 用于解析 YAML 简洁配置格式的关键方法
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    /**
     * 断言逻辑
     * 从请求 URL 中取出某个查询参数（由 param 决定）；
     * 如果该参数值等于配置中的 value，则返回 true，命中断言；
     * 否则该路由不生效。
     */
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                ServerHttpRequest request = serverWebExchange.getRequest();
                String first = request.getQueryParams().getFirst(config.param);
                return StringUtils.hasText(first) && first.equals(config.value);
            }
        };
    }

    /**
     * 配置参数
     * 是谓词的配置载体。
     * 自动映射 YAML 中的参数。
     * 使用 @Validated 和 @NotEmpty 提供参数合法性校验（参数必须有值）。
     */
    @Validated
    public static class Config {

        @NotEmpty
        private String param;

        @NotEmpty
        private String value;

        public @NotEmpty String getParam() {
            return param;
        }

        public void setParam(@NotEmpty String param) {
            this.param = param;
        }

        public @NotEmpty String getValue() {
            return value;
        }

        public void setValue(@NotEmpty String value) {
            this.value = value;
        }

    }

}
/**
 * 关键细节与注意点
 * 细节	    说明
 * 类名规则	必须以 RoutePredicateFactory 结尾，注册名就是前缀部分
 * 自动注入	需要加 @Component 才能被 Gateway 自动扫描识别
 * 参数映射	shortcutFieldOrder 决定参数解析顺序
 * 校验注解	使用 @Validated + @NotEmpty 保障参数合法性
 * 返回类型	必须返回 Predicate<ServerWebExchange> 类型的函数
 * <p>
 * 步骤	                        说明
 * 1️⃣ 创建断言类	                继承 AbstractRoutePredicateFactory<Config>
 * 2️⃣ 编写 apply()	            实现匹配逻辑，返回 Predicate<ServerWebExchange>
 * 3️⃣ 实现参数配置类 Config	    支持字段注入和验证
 * 4️⃣ 实现 shortcutFieldOrder	支持 YAML 简写方式
 * 5️⃣ 加 @Component 注册	        自动让 Spring 管理并启用断言
 */